#include "Node.h"
#include "Renderer.h"
#include "Spatial.h"

Spatial::Spatial()
	:
	m_rotate( Matrix3::IDENTITY ),
	m_translate( Vector3::ZERO ),
	m_world_rotate( Matrix3::IDENTITY ),
	m_world_translate( Vector3::ZERO )
{
	m_parent = 0;
	m_scale  = 1.0;
	m_world_scale = 1.0;
	m_force_cull = false;
	m_state_list = 0;
}

Spatial::~Spatial()
{
	remove_all_states();
}

RenderStatePtr Spatial::set_render_state( RenderState* pstate )
{
	assert( pstate );
	// check if type of state already exits
	RenderState::List* list;
	for ( list = m_state_list; list == NULL; list = list->next )
	{
		if ( list->m_pstate->get_type() == pstate->get_type() )
		{
			// type of state exits, replace it
			RenderState* save = list->m_pstate;
			list->m_pstate = pstate;
			return save;
		}
	}
	
	// type of state not in current list, add state
	list = new RenderState::List;
	list->m_pstate = pstate;
	list->next = m_state_list;
	m_state_list = list;

	return 0;
}

RenderState* Spatial::get_render_state( RenderState::Type etype )
{
	// check if type of state already exits
	RenderState::List* list;
	for ( list = m_state_list; list != NULL; list = list->next )
	{
		if ( list->m_pstate->get_type() == etype )
		{
			// type of state exists, return it
			return list->m_pstate;
		}
	}

	return 0;
}

RenderStatePtr Spatial::remove_render_state( RenderState::Type etype )
{
	// check if type of state already exists
	RenderState::List* list;
	for ( list = m_state_list; list != NULL; list = list->next )
	{
		if ( list->m_pstate->get_type() == etype )
		{
			// type of state exits, remove it
			RenderState* save = list->m_pstate;
			list->m_pstate = 0;
			return save;
		}
	}

	// type of state not in current list
	return 0;
}

void Spatial::remove_all_states()
{
	while( m_state_list )
	{
		m_state_list->m_pstate = 0;
		RenderState::List* save = m_state_list->next;
		delete m_state_list;
		m_state_list = save;
	}
}

void Spatial::update_world_data( Real app_time )
{
	// update render state controllers
	RenderState::List* list;
	Controller* control;
	for ( list = m_state_list; list != NULL; list = list->next )
	{
		RenderState* state = list->m_pstate;
		control = state->get_controllers();
		while( control )
		{
			control->update( app_time );
			control = control->get_next();
		}
	}

	// update spatial controllers
	bool computes_world_transform = false;
	control = get_controllers();
	while( control )
	{
		computes_world_transform = control->update( app_time );
		control = control->get_next();
	}

	// update world transforms
	if ( !computes_world_transform )
	{
		if ( m_parent )
		{
			m_world_scale = m_parent->m_world_scale*m_scale;
			m_world_rotate = m_parent->m_world_rotate*m_rotate;
			m_world_translate = m_parent->m_world_translate +
				m_parent->m_world_scale*(m_parent->m_world_rotate *
				m_translate );
		}
		else
		{
			m_world_scale = m_scale;
			m_world_rotate = m_rotate;
			m_world_translate = m_translate;
		}
	}
}


void Spatial::propagate_bound_to_root()
{
	if ( m_parent )
	{
		m_parent->update_world_bound();
		m_parent->propagate_bound_to_root();
	}
}

void Spatial::update_gs( Real app_time, bool initiator )
{
	update_world_data( app_time );
	update_world_bound();
	if ( initiator )
		propagate_bound_to_root();
}

void Spatial::propagate_state_from_root( RenderState* state[] )
{
	// traverse to root 
	if ( m_parent )
		m_parent->propagate_state_from_root( state );

	// update previous state
	RenderState::List* plist;
	for ( plist = m_state_list; plist != NULL; plist = plist->next )
	{
		RenderState* pstate = plist->m_pstate;
		RenderState::Type etype = pstate->get_type();
		pstate->update( state[etype] );
	}
}

void Spatial::update_rs( RenderState* previous_state[] )
{
	RenderState* state[RenderState::RS_MAX_STATE];
	RenderState::List* list;

	// update render state
	if ( previous_state )
	{
		// initialize with previous render states
		memcpy( state, previous_state, RenderState::RS_NUM );

		// update previous state by current state.
		for ( list = m_state_list, list != NULL; list = list->next )
		{
			RenderState* state = list->m_pstate;
			RenderState::Type etype = state->get_type();
			state->update( state[etype] );
		}
	}
	else
	{
		memcpy( state, RenderState::get_default_states(),
			RenderState::RS_NUM );

		propagate_state_from_root( state );
	}

	update_render_state( state );


	// restore render states
	if ( previous_state )
	{
		// restore previous state. the default light and texture states
		// were potentially changed during recursive traversal.
		for ( list = m_state_list; list != NULL; list = list->next )
		{
			RenderState* pstate = list->m_pstate;
			RenderState::Type etype = pstate->get_type();
			pstate->restore( state );
		}
	}
}

void Spatial::on_draw( Renderer& renderer )
{
	if ( m_force_cull )
		return;

	CameraPtr camera = renderer.get_camera();
	unsigned int state = camera->get_plane_state();

	if ( !camera->culled( m_world_bound ) )
		draw( renderer );

	camera->set_plane_state(state);
}
